/**
 * Copyright 2014 ABSir's Studio
 * 
 * All right reserved
 *
 * Create on 2014-3-18 下午7:26:21
 */
package com.absir.server.socket;

import java.io.Serializable;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListMap;

import com.absir.context.core.ContextBase;
import com.fasterxml.jackson.annotation.JsonIgnore;

/**
 * @author absir
 * 
 */
public class ServerContext extends ContextBase implements IServerContext {

	/** server */
	protected JiServer server;

	/** closed */
	protected boolean closed;

	/** mutil */
	protected boolean mult;

	/** channelContexts */
	@JsonIgnore
	protected Map<Serializable, SocketChannelContext> channelContexts = new ConcurrentSkipListMap<Serializable, SocketChannelContext>();

	/** channelContextMults */
	@JsonIgnore
	protected Map<SocketChannel, SocketChannelContext> channelContextMults;

	/**
	 * @return the server
	 */
	public JiServer getServer() {
		return server;
	}

	/**
	 * @param server
	 *            the server to set
	 */
	public void setServer(JiServer server) {
		this.server = server;
	}

	/**
	 * @return the mult
	 */
	public boolean isMult() {
		return mult;
	}

	/**
	 * @param mult
	 *            the mult to set
	 */
	public void setMult(boolean mult) {
		this.mult = mult;
		if (mult) {
			if (channelContextMults == null) {
				channelContextMults = new ConcurrentHashMap<SocketChannel, SocketChannelContext>();
			}

		} else {
			channelContextMults = null;
		}
	}

	/**
	 * @return the online
	 */
	public long getOnline() {
		return channelContexts.size() + (channelContextMults == null ? 0 : channelContextMults.size());
	}

	/**
	 * @return the closed
	 */
	public boolean isClosed() {
		return closed;
	}

	/**
	 * @param closed
	 *            the closed to set
	 */
	public void setClosed(boolean closed) {
		this.closed = closed;
	}

	/**
	 * @return the channelContexts
	 */
	public Map<Serializable, SocketChannelContext> getChannelContexts() {
		return channelContexts;
	}

	/**
	 * @return the channelContextMults
	 */
	public Map<SocketChannel, SocketChannelContext> getChannelContextMults() {
		return channelContextMults;
	}

	/**
	 * @param id
	 * @param channelContext
	 */
	public synchronized void loginSocketChannelContext(Serializable id, SocketChannelContext channelContext) {
		channelContext.setId(id);
		SocketChannelContext context = channelContexts.put(id, channelContext);
		if (context != null) {
			if (mult) {
				channelContextMults.put(channelContext.getSocketChannel(), channelContext);

			} else {
				SocketServer.close(context.getSocketChannel());
			}
		}
	}

	/**
	 * @param id
	 * @param socketChannel
	 * @return
	 */
	public synchronized SocketChannelContext logoutSocketChannelContext(Serializable id, SocketChannel socketChannel) {
		if (mult) {
			SocketChannelContext context = channelContexts.get(id);
			if (context == null || context.getSocketChannel() != socketChannel) {
				return channelContextMults.remove(socketChannel);

			} else {
				return channelContexts.remove(id);
			}

		} else {
			SocketChannelContext context = channelContexts.remove(id);
			if (context != null && context.getSocketChannel() != socketChannel) {
				channelContexts.put(id, context);
				context = null;
			}

			return context;
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.absir.context.core.ContextBase#getLifeTime()
	 */
	@Override
	protected long getLifeTime() {
		return SocketServerContext.getIdleTimeout();
	}

	/**
	 * @param contextTime
	 * @return
	 */
	public boolean stepDone(long contextTime) {
		if (super.stepDone(contextTime)) {
			retainAt(contextTime);
			Iterator<Entry<Serializable, SocketChannelContext>> iterator = channelContexts.entrySet().iterator();
			while (iterator.hasNext()) {
				Entry<Serializable, SocketChannelContext> entry = iterator.next();
				SocketChannelContext socketChannelContext = entry.getValue();
				if (!socketChannelContext.getSocketChannel().isConnected()) {
					iterator.remove();
					SocketServer.close(socketChannelContext.getSocketChannel());
				}
			}

			if (mult) {
				Iterator<Entry<SocketChannel, SocketChannelContext>> setIterator = channelContextMults.entrySet().iterator();
				while (setIterator.hasNext()) {
					SocketChannelContext socketChannelContext = setIterator.next().getValue();
					if (!socketChannelContext.getSocketChannel().isConnected()) {
						setIterator.remove();
						SocketServer.close(socketChannelContext.getSocketChannel());
					}
				}
			}
		}

		return false;
	}
}
